from cocos import sprite, text, menu
import pyglet
import config
from events import fj_event_dispatcher
import random
from cocos import collision_model
import pygame

'''
定义本游戏中的常用对象，包括：
1. 飞机hero
2. 子弹bullet
3. 敌机enemy
4. 游戏过程中用来显示总分的文本标签
'''


class Hero(sprite.Sprite):
    def __init__(self):
        '''
        初始化飞机属性包括
        1. 相关图片
        2. 大小
        3. 飞机动画（长短喷火）
        4. 碰撞属性cshape
        '''
        fj_event_dispatcher.push_handlers(self)
        frames = []
        for x in range(1, 3):
            frame = pyglet.resource.image('imgs/hero/hero%d.png' % x)
            frames.append(frame)
        self.animation = pyglet.image.Animation.from_image_sequence(sequence=frames,
                                                               period=0.5,
                                                               loop=True)
        # 定义飞机动画
        super(Hero, self).__init__(self.animation)

        self.position = (config.WIN_WIDTH // 2, 100)
        self.scale = 0.5

        # 添加一个属性（用作移动探测）
        self.is_movable = False

        # 碰撞属性
        self.cshape = collision_model.AARectShape(center=(collision_model.eu.Vector2(*self.position)),
                                                  half_width=self.width // 2,
                                                  half_height=self.height // 2)

    def update_position(self, x, y):
        # 飞机能够根据传入的坐标值进行移动
        self.position = (x, y)
        # 碰撞检测跟随
        self.cshape.center = collision_model.eu.Vector2(*self.position)

    def contains(self, x, y):
        '''
        解决原来contains不准确的问题
        :param x: 传入的鼠标x坐标值
        :param y: 传入的鼠标y坐标值
        :return: hero是否被选中（选中：True；未选中：False）
        '''
        if super(Hero, self).contains(x, y):
            return True

        # ========范围圈定=========#
        fj_x, fj_y = self.position
        left = fj_x - self.width // 2
        right = fj_x + self.width // 2
        bottom = fj_y - self.height // 2
        top = fj_y + self.height // 2
        # =======================#

        if x >= left and x <= right and y >= bottom and y <= top:
            return True
        else:
            return False

    def explode(self):
        '''
        英雄爆炸
        1. 播放爆炸动画
        2. 播放爆炸音效
        2. 播放完成后，通知on_game_over事件
        :return:none
        '''
        frames = []
        for x in range(3, 6):
            frame = pyglet.resource.image('imgs/hero/hero%d.png' % x)
            frames.append(frame)
        self.image = pyglet.image.Animation.from_image_sequence(sequence=frames,
                                                                period=0.1,
                                                                loop=False)

        # 音效部分
        sound = pygame.mixer.Sound('sounds/hero_die.ogg')
        sound.play()

        # 通知游戏结束
        self.schedule_interval(self._game_over_schedule, interval=1)

    def _game_over_schedule(self, dt):
        '''
        游戏结束，包括：
        1. 从父节点移除飞机
        2. 发送on_game_over消息
        3. 取消事件调度
        :return:
        '''
        self.kill()
        fj_event_dispatcher.dispatch_event('on_game_over')

        self.unschedule(self._game_over_schedule)

    def on_game_pause(self):
        # print('Hero receive on_game_pause')
        self.image = pyglet.resource.image('imgs/hero/hero1.png')

    def on_game_resume(self):
        self.image = self.animation


class Bullet(sprite.Sprite):
    '''
    子弹类：
    1.子弹属性（形状、大小等）~子弹位置根据飞机位置而定，可以在layer中设置
    2.子弹运动（稳定前行）
    '''

    def __init__(self):
        # 设定子弹位置和大小
        super(Bullet, self).__init__('imgs/bullet.png')
        self.scale = 0.5
        self.cshape = collision_model.AARectShape(center=collision_model.eu.Vector2(*self.position),
                                                  half_width=self.width // 2,
                                                  half_height=self.height // 2)

    def bullet_move_schedule(self, dt):
        # 周期性更新位置，当飞出屏幕后，停止调度（节约CPU，并通知layer回收该子弹（消息发送）
        if self.y > config.WIN_HEIGHT + self.height // 2:
            # 停止调度（节约CPU)
            self.unschedule(self.bullet_move_schedule)

            # 发送“子弹可重用”事件消息
            fj_event_dispatcher.dispatch_event('on_bullet_reusable', self)
        else:
            self.y += 40
            self.position = (self.x, self.y)
            # 碰撞区域跟随
            self.cshape.center = collision_model.eu.Vector2(*self.position)

    def reset_status(self, fj_x, fj_y, fj_width, fj_height):
        # 解决子弹回收后，不在前进的问题，意味着，子弹重新使用前需要将事件调度重置
        self.unschedule(self.bullet_move_schedule)
        self.x = fj_x
        self.y = fj_y + fj_height // 2 + self.height
        # 碰撞区域跟随
        self.cshape.center = collision_model.eu.Vector2(*self.position)
        self.schedule_interval(self.bullet_move_schedule, interval=0.1)

    def explode(self):
        '''
        子弹发生碰撞时候的行为：
        1. 从画面中移除
        2. 停止被调度
        2. 发出‘on_bullet_reusable'消息
        :return:None
        '''
        # 位置更改
        self.position = (0, -self.width)
        self.cshape.center = collision_model.eu.Vector2(*self.position)

        # 停止调度
        self.unschedule(self.bullet_move_schedule)

        # 发出消息
        fj_event_dispatcher.dispatch_event('on_bullet_reusable', self)



class Enemy(sprite.Sprite):
    def __init__(self):
        '''
        初始化敌机，包括：
        1. 图片
        2. 位置
        3. 大小
        4. 飞行调度
        5.碰撞范围
        '''
        super(Enemy, self).__init__('imgs/enemy/enemy_small1.png')
        self.scale = 0.5
        self.cshape = collision_model.AARectShape(center=collision_model.eu.Vector2(*self.position),
                                                  half_width=self.width // 2,
                                                  half_height=self.height // 2)
        fj_event_dispatcher.push_handlers(self)

    def rush_out_schedule(self, dt):
        '''
        冲出屏幕后：
        1.取消调度
        2.发送可重用消息
        '''
        if self.y < -self.height // 2:
            self.unschedule(self.rush_out_schedule)
            fj_event_dispatcher.dispatch_event('on_enemy_reusable', self)
        else:
            self.y -= 8
            self.cshape.center = collision_model.eu.Vector2(*self.position)
        # self.position = self.x, self.y

    def reset_status(self):
        '''
        重新设置敌机的状态，将位置、调度等参数重置
        :return: None
        '''
        self.position = (random.randint(self.width // 2, config.WIN_WIDTH - self.width // 2),
                         config.WIN_HEIGHT + self.height // 2)
        self.cshape.center = collision_model.eu.Vector2(*self.position)

        self.schedule_interval(self.rush_out_schedule, interval=0.02)

    def explode(self):
        '''
        敌机爆炸
        1.停止飞行动作
        2.播放爆炸动画（播放完后移除当前画面，通知可复用）
        3.播放爆炸声音
        :return:none
        '''
        self.unschedule(self.rush_out_schedule)

        frames = []
        for x in range(2, 5):
            frame = pyglet.resource.image('imgs/enemy/enemy_small%d.png' % x)
            frames.append(frame)
        self.image = pyglet.image.Animation.from_image_sequence(sequence=frames,
                                                                period=0.1,
                                                                loop=False)
        # 播放声音
        pygame.mixer.Sound('sounds/enemy_die.ogg').play()

        # 通过调度方式进行去除
        self.schedule_interval(self.after_explode, interval=0.1)

    def after_explode(self, dt):
        '''
        用来处理飞机爆炸后事宜
        1. 移除到飞机外
        2. 重新将飞机的图片设置成飞机
        3. 通知飞机可复用
        4. 取消事件调度（after_explode）
        :param dt:时间调度
        :return:none
        '''
        self.position = (0, config.WIN_HEIGHT + self.height)
        self.image = pyglet.resource.image('imgs/enemy/enemy_small1.png')
        self.unschedule(self.after_explode)
        fj_event_dispatcher.dispatch_event('on_enemy_reusable', self)


class ScoreLabel(text.Label):
    def __init__(self, score=0):
        super(ScoreLabel, self).__init__(str(score), position=(80, config.WIN_HEIGHT - 50),
                                         font_name='Microsoft YaHei',
                                         font_size=20,
                                         color=(10, 10, 10, 255))
        self._score = score

    def update_scores(self, score):
        self._score += score
        # 更新分数到页面
        self.element.text = "%d" % self._score

    def get_scores(self):
        return self._score
